package com.jsy.common.util.publicApi.aksk;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import sun.misc.BASE64Encoder;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

/**
 * gates.huang
 * 2025/07/15
 * AK/SK客户端工具，提供加密,使用时提供给第三方调用者
 */
public class AkSkClient {
    private final String accessKey;
    private final String secretKey;
    private final String apiEndpoint;
    private final HttpClient httpClient;

    public AkSkClient(String accessKey, String secretKey, String apiEndpoint) {
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.apiEndpoint = apiEndpoint;
        this.httpClient = HttpClients.createDefault();
    }

    /**
     * 发送GET请求
     */
    public String sendGetRequest(String path, Map<String, String> queryParams) throws IOException {
        String canonicalQueryString = buildCanonicalQueryString(queryParams);
        String fullUrl = apiEndpoint + path + (canonicalQueryString.isEmpty() ? "" : "?" + canonicalQueryString);

        long timestamp = System.currentTimeMillis() / 1000;
        String nonce = UUID.randomUUID().toString();

        String canonicalRequest = buildCanonicalRequest("GET", path, queryParams, null, timestamp, nonce);
        String signature = generateSignature(canonicalRequest);

        HttpGet request = new HttpGet(fullUrl);
        request.addHeader("x-date", String.valueOf(timestamp));
        request.addHeader("x-nonce", nonce);
        request.addHeader("x-access-key", accessKey);
        request.addHeader("x-signature", signature);
        request.addHeader("content-type", "application/json");

        HttpResponse response = httpClient.execute(request);
        return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
    }

    /**
     * 发送POST请求
     */
    public String sendPostRequest(String path, Map<String, String> queryParams, String requestBody) throws IOException {
        String canonicalQueryString = buildCanonicalQueryString(queryParams);
        String fullUrl = apiEndpoint + path + (canonicalQueryString.isEmpty() ? "" : "?" + canonicalQueryString);

        long timestamp = System.currentTimeMillis()/1000;
        String nonce = UUID.randomUUID().toString();
        String requestBodyHash = hashRequestBody(requestBody);

        String canonicalRequest = buildCanonicalRequest("POST", path, queryParams, requestBodyHash, timestamp, nonce);
        String signature = generateSignature(canonicalRequest);

        HttpPost request = new HttpPost(fullUrl);
        request.addHeader("x-date", String.valueOf(timestamp));
        request.addHeader("x-nonce", nonce);
        request.addHeader("x-access-key", accessKey);
        request.addHeader("x-signature", signature);
        request.addHeader("content-type", "application/json");
        
        if (requestBody != null) {
            request.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8));
        }

        HttpResponse response = httpClient.execute(request);
        return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
    }

    private String buildCanonicalQueryString(Map<String, String> queryParams) {
        if (queryParams == null || queryParams.isEmpty()) {
            return "";
        }

        // 将 EntrySet 转换为 List 以便排序
        List<Map.Entry<String, String>> entries = new ArrayList<>(queryParams.entrySet());

        // 按键排序
        Collections.sort(entries, new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> e1, Map.Entry<String, String> e2) {
                return e1.getKey().compareTo(e2.getKey());
            }
        });

        // 手动拼接字符串
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : entries) {
            if (sb.length() > 0) {
                sb.append("&");
            }
            sb.append(encodeURIComponent(entry.getKey()))
                    .append("=")
                    .append(encodeURIComponent(entry.getValue()));
        }

        return sb.toString();
    }


    /**
     * 构建规范请求
     */
    private String buildCanonicalRequest(String httpMethod, String path,
                                                Map<String, String> queryParams, String requestBodyHash,
                                                long timestamp, String nonce) {
        String canonicalQueryString = buildCanonicalQueryString(queryParams);

        SortedMap<String, String> canonicalHeaders = new TreeMap<>();
        canonicalHeaders.put("x-date", String.valueOf(timestamp));
        canonicalHeaders.put("x-nonce", nonce);
        canonicalHeaders.put("x-access-key", "dummy-value");

        StringBuilder canonicalHeadersStringBuilder = new StringBuilder();
        for (Map.Entry<String, String> entry : canonicalHeaders.entrySet()) {
            canonicalHeadersStringBuilder.append(entry.getKey().toLowerCase())
                    .append(":")
                    .append(entry.getValue().trim())
                    .append("\n");
        }
        String canonicalHeadersString = canonicalHeadersStringBuilder.toString();
        if (canonicalHeadersString.endsWith("\n")) {
            canonicalHeadersString = canonicalHeadersString.substring(0, canonicalHeadersString.length() - 1);
        }

        StringBuilder signedHeadersBuilder = new StringBuilder();
        for (String key : canonicalHeaders.keySet()) {
            if (signedHeadersBuilder.length() > 0) {
                signedHeadersBuilder.append(";");
            }
            signedHeadersBuilder.append(key.toLowerCase());
        }
        String signedHeaders = signedHeadersBuilder.toString();

        StringBuilder canonicalRequestBuilder = new StringBuilder();
        canonicalRequestBuilder.append(httpMethod).append("\n")
                .append(path).append("\n")
                .append(canonicalQueryString).append("\n")
                .append(canonicalHeadersString).append("\n")
                .append("").append("\n")
                .append(signedHeaders).append("\n")
                .append(requestBodyHash);
        return canonicalRequestBuilder.toString();
    }
    /**
     * 生成签名
     */
    private String generateSignature(String canonicalRequest) {
        try {
            Mac hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(
                    secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
            hmac.init(secretKeySpec);
            byte[] signatureBytes = hmac.doFinal(canonicalRequest.getBytes(StandardCharsets.UTF_8));
            BASE64Encoder base64Encoder = new BASE64Encoder();
            return base64Encoder.encode(signatureBytes);
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate signature", e);
        }
    }

    /**
     * 计算请求体哈希
     */
    private String hashRequestBody(String requestBody) {
        if (requestBody == null) {
            requestBody = "";
        }
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(requestBody.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hashBytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to hash request body", e);
        }
    }

    /**
     * 字节转十六进制
     */
    private String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }

    /**
     * URI组件编码
     */
    private String encodeURIComponent(String value) {
        if (value == null) {
            return "";
        }
        try {
            return java.net.URLEncoder.encode(value, StandardCharsets.UTF_8.toString())
                .replace("+", "%20")
                .replace("*", "%2A")
                .replace("%7E", "~");
        } catch (Exception e) {
            throw new RuntimeException("Failed to encode URI component", e);
        }
    }

    public static void main(String[] args) throws IOException {
        Map<String, String> queryParams = new HashMap<>();
        AkSkClient client = new AkSkClient("AK-r77DPoTg77RzzOkHHdnFNA==", "YvqmR07WVhPoG8OKaKlAVktFUKUbTs+DbFF2MVJhYJc=", "http://localhost:8090");
        String response = client.sendGetRequest("/app/api/training/data/country", null);
        System.out.println(response);
        String requestBody = "{\n" +
                "    \"startTime\": \"2025-01-27\",\n" +
                "    \"endTime\": \"2025-07-13\",\n" +
                "    \"isFull\": false,\n" +
                "    \"salesType\":1\n" +
                "}";
        response = client.sendPostRequest("/app/api/training/data/country", queryParams, requestBody);
        System.out.println(response);
    }

}